package pr3.cromosoma;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import pr3.funcion.Funcion;

public class Estado implements Cloneable{
    
    private static final String OBJETIVO = "UNIVERSAL";

	private ArrayList<Character> pila = new ArrayList<Character>(OBJETIVO.length());
	private ArrayList<Character> mesa = new ArrayList<Character>(OBJETIVO.length());
    private int bs;
    private Character sn;

    public ArrayList<Character> getMesa() {
        return mesa;
    }

    public void setMesa(List<Character> mesa) {
        this.mesa.clear();
        Collections.copy(this.mesa, mesa);
    }

    public ArrayList<Character> getPila() {
        return pila;
    }

    public void setPila(List<Character> pila) {
        this.pila.clear();
        Collections.copy(this.pila, pila);
    }
    
    public Character getCP() {
        if(pila.isEmpty())
            return null;
        return pila.get(pila.size() - 1);
    }
    
    public Character getBS() {
        if(bs == -1) {
            return null;
        }
        return pila.get(bs);
    }
    
    public Character getSN() {
        return sn;
    }
    
    public boolean isParcialmenteCorrecto() {
        return bs == pila.size() - 1;
    }
    
    public boolean isCorrecto() {
        return isParcialmenteCorrecto() && pila.size() == OBJETIVO.length();
    }
    
    public void calcularTerminales() {
        bs = 0;
        for(Character c : pila) {
            if(c.equals(OBJETIVO.charAt(bs)))
                bs++;
        }
        if(bs == OBJETIVO.length()) { //hemos acabado
            sn = null;
        }
        else {
            sn = OBJETIVO.charAt(bs);
        }
        bs--;
    }
    
    public Character getCharacter(Funcion.Terminal terminal) {
        switch(terminal) {
            case BS:
                return getBS();
            case CP:
                return getCP();
            case SN:
                return getSN();
            default:
                throw new IllegalArgumentException(terminal + " no es un terminal valido");
        }
        
    }
    
    public boolean evaluarMP(Funcion.Terminal elemento) {
        return evaluarMP(getCharacter(elemento));
    }
    
    public boolean evaluarMP(Character elemento) {
        if(elemento == null || !mesa.contains(elemento)) {
            return false;
        }
        if(isParcialmenteCorrecto() && elemento.equals(sn)) { 
            bs++;
            if(bs >= OBJETIVO.length() - 1) { //hemos acabado
                sn = null;
            }
            else {
                sn = OBJETIVO.charAt(bs);
            }
        }
        mesa.remove(elemento);
        pila.add(elemento);
        
        return true;
    }
    
    public boolean evaluarMM(Funcion.Terminal elemento) {
        return evaluarMM(getCharacter(elemento));
    }
    
    public boolean evaluarMM(Character elemento) {
        if(elemento == null || !pila.contains(elemento)) {
            return false;
        }
        if(isParcialmenteCorrecto()) { //apuntabamos a la posicion cabecera
            sn = getBS();
            bs--;
        }
        mesa.add(elemento);
        pila.remove(pila.size() - 1);
        return true;
    }
    
	@Override
	public Estado clone() {
        Estado clon = new Estado();
        clon.pila.clear();
        for(Character c : pila) {
            clon.pila.add(c);
        }
        clon.mesa.clear();
        for(Character c : mesa) {
            clon.mesa.add(c);
        }
        calcularTerminales();
        return clon;
	}
    
    public static char getRandomChar() {
        return OBJETIVO.charAt((int)(Math.random() * OBJETIVO.length()));
    }

    void copiar(Estado e) {
        pila.clear();
        for(Character c : e.pila) {
            pila.add(c);
        }
        mesa.clear();
        for(Character c : e.mesa) {
            mesa.add(c);
        }
        calcularTerminales();
    }
	
}
